package connection

import (
	"errors"
	"log"
	"net"
	"sync"
	"time"
	//"src/ProtoModel"
	"src/model"
)

// websocket 链接
type Tcp struct {
	//连接唯一ID
	Id string
	//是否已经登录完成绑定
	Uid string
	//昵称
	Nickname string
	//头像
	Avatar string
	//用户设备id 例如 web,h5,ios
	Did string
	//所在房间号
	RoomNo string
	//已登录用户绑定信息
	User model.ConnUserinfoModel
	//连接
	TcpConn   *net.Conn
	InChan    chan []byte
	OutChan   chan []byte
	CloseChan chan byte
	Times     int64
	Mutex     sync.Mutex
	IsClosed  bool
}

// 这是更新心跳有效时间函数
func (this *Tcp) UpdateTime() {
	this.Times = time.Now().Unix()
}

func (this *Tcp) GetConn(wsConn *net.Conn) (conn *Tcp, err error) {
	conn = &Tcp{
		User:      model.ConnUserinfoModel{},
		TcpConn:   wsConn,
		InChan:    make(chan []byte, 1024),
		OutChan:   make(chan []byte, 1024),
		CloseChan: make(chan byte, 1),
	}
	//读协程
	go conn.ReadLoop()
	//写协程
	go conn.WriteLoop()
	return conn, err
}

func (conn *Tcp) ReadMess() (data []byte, err error) {
	select {
	case data = <-conn.InChan:
	case <-conn.CloseChan:
		err = errors.New("connection is closed")
	}
	return
}

func (conn *Tcp) WriteMes(data []byte) (err error) {
	select {
	case conn.OutChan <- data:
	case <-conn.CloseChan:
		err = errors.New("connection is closed")
	}
	return
}

func (conn *Tcp) Close() {
	//加锁，只能执行一次
	conn.Mutex.Lock()
	if !conn.IsClosed {
		_ = (*conn.TcpConn).Close()
		//本身线程安全，可重入
		close(conn.CloseChan)
		conn.IsClosed = true
	}
}

// 具体实现读消息 每条消息最大1024字节限制
func (conn *Tcp) ReadLoop() {

	for {
		var (
			n int
			//data []byte
			err error
		)

		// 创建切片
		buf := make([]byte, 1024)

		n, err = (*conn.TcpConn).Read(buf)
		if err != nil {
			continue
		}
		if n == 0 {
			continue
		}
		//data := make([]byte, n)
		//data = buf[:n]
		select {
		case conn.InChan <- buf[:n]:
		case <-conn.CloseChan:
			goto ERR
		}
	}
ERR:
	conn.Close()
}

// 具体实现写消息 websocket.BinaryMessage 是二进制类型
func (conn *Tcp) WriteLoop() {
	var (
		data []byte
		err  error
	)
	for {
		select {
		case data = <-conn.OutChan:
		case <-conn.CloseChan:
			goto ERR
		}
		_, err = (*conn.TcpConn).Write(data)
		if err != nil {
			log.Println(err)
			goto ERR
		}
	}
ERR:
	conn.Close()
}
